/*
Copyright (c) 2008 Ryan BB Schreiber, Brendan G Fry

Permission is hereby granted, free of charge, to any person obtaining a copy of 
this software and associated documentation files (the "Software"), to deal in 
the Software without restriction, including without limitation the rights to 
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
of the Software, and to permit persons to whom the Software is furnished to do 
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all 
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using Commons.Collections;
using Darkside.GeneratorStudio.Library.Domain;
using Darkside.GeneratorStudio.Library.Events;
using log4net;
using NVelocity;
using NVelocity.App;
using NVelocity.Context;

namespace Darkside.GeneratorStudio.Library.Generator
{
    public class ModelGenerator
    {
        private static readonly ILog _Log = LogManager.GetLogger(typeof(ModelGenerator));
        private readonly bool _MakePartial;
        private readonly Project _project;
        private readonly bool _PropChange;

        private readonly VelocityEngine engine;

        public ModelGenerator()
        {
        }

        public ModelGenerator(Project project)
        {
            _project = project;
            //TODO: Add project support for partials
            _MakePartial = true;
            //TODO: Add project support for propery changes
            _PropChange = false;
            //TODO: Add project support for validations

            // Initialize NVelocity
            engine = new VelocityEngine();
            var props = new ExtendedProperties();

            //Get a list off all template paths
            var templatePaths = new List<string>
                                    {
                                        "."
                                    };

            string path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            foreach (TemplateFile template in project.TemplateFiles)
            {
                if (!templatePaths.Contains(template.Path.Replace("\\" + template.Title + ".dgt", "")))
                {
                    templatePaths.Add(template.Path.Replace("\\" + template.Title + ".dgt", ""));
                }
            }

            if (!templatePaths.Contains(path + @"\Templates"))
                templatePaths.Add(path + @"\Templates");

            props.AddProperty("file.resource.loader.path", new ArrayList(templatePaths));
            engine.Init(props);
        }

        /// <summary>
        /// Generate the model(s) from the template(s) selected...
        /// </summary>
        public void GenerateClasses()
        {
            int templateTotalCount = _project.TemplateFiles.Count;
            int tableTotalCount = _project.Tables.Count;
#pragma warning disable 219
            int templateCount = 0; //this will be an incremented and used as an offset later...
            int tableCount = 0;
#pragma warning restore 219
            int totalTasks = templateTotalCount * tableTotalCount;
            int tasksCompleted = 0; // this will allow the progress bar to reflect the correct value.
            foreach (TemplateFile templateFile in _project.TemplateFiles)
            {
                templateCount++;
                string templateFileName = templateFile.Title + ".dgt";

                Template template = engine.GetTemplate(templateFileName);
                foreach (Table table in _project.Tables)
                {
                    tableCount++;
                    tasksCompleted++;
                    if (!table.Settings.Active)
                        continue;
                    // Generate ActiveRecord Classes
                    GlobalEvents.SetStatus(null, tasksCompleted, totalTasks, String.Format("Transforming template [{0}] for object [{1}]", templateFile.Title, table.Name));
                    
                    _Log.DebugFormat("Generating {0} for {1}", table.Name,templateFile.Title);

                    var context = new VelocityContext();

                    //Add all the properties needed for the VTL templates
                    context.Put("projectnamespace", _project.ProjectNameSpace);
                    context.Put("solutionnamespace", _project.SolutionNameSpace);
                    context.Put("developer", Environment.UserName);
                    context.Put("Partial", _MakePartial ? "partial" : "");
                    context.Put("PropChange", _PropChange);
                    context.Put("table", table);
                    context.Put("columns", table.Columns);
                    context.Put("parenttables", table.ParentTables);
                    context.Put("childtables", table.ChildTables);
                    context.Put("date", DateTime.Now.ToString("yyyy-MM-dd"));

                    //get suggested class name
                    string fileName = GetFileName(context, templateFile);

                    if (!CanWriteThisFile("", "", "")) return;
                    if (!Directory.Exists(_project.OutputPath + @"\" + templateFile.Title))
                        Directory.CreateDirectory(_project.OutputPath + @"\" + templateFile.Title);
                    var streamWriter = new StreamWriter(_project.OutputPath + @"\" + templateFile.Title + @"\" + fileName);
                    try
                    {
                        var stringWriter = new StringWriter();
                        template.Merge(context, stringWriter);
                        streamWriter.WriteLine(stringWriter.GetStringBuilder().ToString());
                    }
                    finally
                    {
                        streamWriter.Close();
                    }
                }
            }
        }

        private string GetFileName(IContext context, TemplateFile template)
        {
            string templateFullFileName = template.Path;
            string fileNameTemplate = TemplateFile.GetTemplateProperty(templateFullFileName, "FILENAME");
            string fileName;
            using (var stringReader = new StringReader(fileNameTemplate))
            {
                using (var stringWriter = new StringWriter())
                {
                    engine.Evaluate(context, stringWriter, null, stringReader);
                    fileName = stringWriter.GetStringBuilder().ToString();
                }
            }
            return fileName;
        }

        /// <summary>
        /// Ask the user how to handle file collisions
        /// </summary>
        /// <param name="outputDir"></param>
        /// <param name="className"></param>
        /// <param name="fileName"></param>
        /// <returns></returns>
        //TODO: To do.
        private bool CanWriteThisFile(string outputDir, string className, string fileName)
        {
            return true;
        }
    }
}